; rdmain.asm
; RAM Disk main

	.include "rdglobal.inc"
	.include "rdmem.inc"
	.include "rddir.inc"
	.include "rdio.inc"
	.include "rdcmd.inc"
	.include "rdrel.inc"
	_IN_RDMAIN = 1
	.include "rdmain.inc"
	
	MAX_OPEN_FILES = 10
	
.macro ERROR_RETURN code
			lda #code
			sec
			rts
.endmacro

	.segment "DATA"

finfent_lo:		.res MAX_OPEN_FILES		; ptr to dir entry
finfent_hi:		.res MAX_OPEN_FILES
finfmode:		.res MAX_OPEN_FILES		; I/O mode
finfbank:		.res MAX_OPEN_FILES		; current block address (bank or address low)
finfaddr:		.res MAX_OPEN_FILES		; current block address (address high)
finfpos:		.res MAX_OPEN_FILES		; current read/write position
finflsze:		.res MAX_OPEN_FILES		; low byte of size for reading
finfchn:		.res MAX_OPEN_FILES		; channel for file

fileno: 		.res MAX_OPEN_FILES		; LFN

finidx:			.res 256				; LFN |-> info index

actinfo:		.res 1					; temp. info index
actchn:			.res 1					; temp. channel

	.segment "CODE"

;----------------------------------------------------------
; Initialisation
;----------------------------------------------------------
	
fn_init:	; initialise storage
			jsr md_init
			lda #FIRST_DATA_BANK
@maddloop:	jsr md_addbank
			clc
			adc #1
			cmp #LAST_DATA_BANK
			bne @maddloop
			; initialise directory
			jsr dir_init
			; initialise status
			ldx #MAX_OPEN_FILES
			lda #0
@initloop:	sta fileno-1, x
			dex
			bne @initloop
			jsr initinfo
			; init i/o
			jsr io_init
			; init rel handling
			jsr rel_init
			; no error
			clc
			rts

;----------------------------------------------------------
; Kernel hooks
;----------------------------------------------------------

	.segment "TABLE"
	
funptr:		.res 2

	
	.segment "CODE"

;------;
; OPEN ;
;------;

fn_open:	lda $B9						; check for command channel	
			cmp #CMD_CHANNEL
			bne @checkfn
			jmp opencmd
@checkfn:	lda $B7						; file name empty?
			bne @notnull
			ERROR_RETURN ERR_MISSING_FILENAME
@notnull:	jsr parse_name				; parse file name
			bcc @do_open
			jmp not_found
@do_open:	lda #STATUS_OK
			jsr cmd_setst
			lda param1+1				; check for relative file
			beq @chkchn
			sta ptr+1
			lda param1
			sta ptr
			ldy #0
			lda (ptr), y
			cmp #'l'
			bne	@chkmode
			jmp openrel	
@chkmode:	lda param2+1				; check open mode
			beq	@chkchn					; not given, check channel
			sta ptr+1
			lda param2
			sta ptr
			ldy #0
			lda (ptr), y
@chkr:		cmp #'r'					; ",R" ?
			bne @chkw
			jmp openrd
@chkw:		cmp #'w'					; ",W" ?
			bne @chka
			jmp openwr
@chka:		cmp #'a'					; ",A" ?
			bne not_found
			jmp openap
@chkchn:	lda $B9
			bne @chkchn1
			jmp openrd
@chkchn1:	cmp #1
			bne not_found
			jmp openwr

not_found:	lda #2			; ST := Timeout
			jsr $FE6A
			lda #STATUS_FILE_NOT_FOUND
			jsr cmd_setst
			ERROR_RETURN ERR_FILE_NOT_FOUND

;-------;
; CLOSE ;
;-------;
			
fn_close:	stx save_x
			sty save_y
			lda $B8
			jsr findinfo
			bcs @done			; fileno not found -> ignore.
			lda device_no
			cpx outinfo+FileInfo::index
			bne @noout
			jsr getinfout
			txa
			ldx #0
			jsr io_setout
			tax
			lda #$FF
			sta outinfo+FileInfo::index
			lda #3
			sta $9A
@noout:		cpx ininfo+FileInfo::index
			bne @noin
			jsr getinfin
			txa
			ldx #0
			jsr io_setin
			tax
			lda #$FF
			sta ininfo+FileInfo::index
			lda #0
			sta $99
@noin:		jsr clsfil
@done:		ldx save_x
			ldy save_y
			clc
			rts 

clsfil:		lda finfmode, x
			cmp #IO_MODE_REL
			bne @norel
			jsr rel_close
@norel:		lda #0
			sta fileno, x
			lda finfent_lo, x	; set ptr to directory entry
			sta ptr
			lda finfent_hi, x
			sta ptr+1
			ldy #DirEntry::type
			lda (ptr), y
			ora #FILE_FLAG_OPEN
			sta (ptr), y
			rts			

;-------;
; CHKIN ;
;-------;

fn_chkin:	; save registers
			stx save_x
			sty save_y
			; find file info
			lda $B8
			jsr findinfo
			bcs not_open			; fileno not found -> ignore.
			stx actinfo
			; setup input
			jsr setinfin
			lda finfmode, x
			tax
			jsr io_setin
			jsr io_prepin
			; restore registers
			ldx save_x
			ldy save_y
			clc
			rts

not_open:	ERROR_RETURN ERR_FILE_NOT_OPEN

;-------;
; CKOUT ;
;-------;

fn_ckout:	; save registers
			stx save_x
			sty save_y
			; find file info
			lda $B8
			jsr findinfo
			bcs not_open			; fileno not found -> ignore.
			stx actinfo
			; setup output
			jsr setinfout
			lda finfmode, x
			tax
			jsr io_setout
			jsr io_prepout
			; restore registers
			ldx save_x
			ldy save_y
			clc
			rts

;-------;
; CLRCH ;
;-------;

fn_clrch:	stx save_x
			ldx device_no
			cpx $9A
			bne @noout
			lda #3
			sta $9A
@noout:		cpx $99
			bne @noin
			lda #0
			sta $99						 
@noin:		ldx #0
			jsr io_setin
			jsr io_setout
			jsr getinfin
			jsr getinfout
			jsr clrinfo
			lda #0
			ldx save_x
			jsr cmd_clrch
			jmp rel_clrch

;-------;
; CLALL ;
;-------;

fn_clall:	jsr fn_clrch
			sty save_y
			ldx #0
@clsloop:	lda fileno, x
			beq @goon
			jsr clsfil
@goon:		inx
			cpx #MAX_OPEN_FILES
			bne @clsloop
			ldx save_x
			ldy save_y
			clc
			rts

;----------------------------------------------------------
; Helpers
;----------------------------------------------------------

	.segment "DATA"

filename:	.res 2
prefix:		.res 2
param1:		.res 2	
param2:		.res 2	

	.segment "CODE"
	
; Parse file name parameter for "open"
;	INPUT: 	(fname_buf) ... file name (format: [prefix:]name[,param1[,param2]])
;			($B7) 		... length of file name
;	OUTPUT:	(filename)	... address of file name
;			(prefix)	... address file name prefix ("xx:") or < $0100
;			(param1)	... address or first parameter (",p1") or < $0100
;			(param2)	... address or first parameter (",p2") or < $0100
;	RETURN:	Carry ... Success (0) or file name is empty (1)
;
parse_name:	lda $B7
			bne @notnull
			jmp @fail
@notnull:	lda #<fname_buf
			sta filename
			lda #>fname_buf
			sta filename+1
			ldx #0				; init prefix, param1, param2 to 0
			stx prefix+1
			stx param1+1
			stx param2+1
@scan_pre:	lda fname_buf, x
			cmp #':'
			bne @chkparm
			lda param1+1		; file name already detected?
			ora prefix+1		; prefix already detected?
			bne @fail
			lda #<fname_buf		; set ptr to prefix
			sta prefix
			lda #>fname_buf
			sta prefix+1
			lda #0				; terminate string
			sta fname_buf, x
			sec					; advance ptr to file name
			txa					
			adc filename
			sta filename
			bcc @next
			inc filename+1
			jmp @next
@chkparm:	cmp #','
			bne @next
			lda #0				; terminate string
			sta fname_buf, x
			lda param1+1
			bne @chkpar2
			sec					; calc ptr to param
			txa
			adc #<fname_buf
			sta param1
			lda #>fname_buf
			adc #0
			sta param1+1
			jmp @next
@chkpar2:	lda param2+1
			bne @fail
			sec					; calc ptr to param
			txa
			adc #<fname_buf
			sta param2
			lda #>fname_buf
			adc #0
			sta param2+1
			jmp @next
@next:		inx					; next character
			cpx $B7
			bne @scan_pre		; loop
			lda #0				; terminate string
			sta fname_buf, x
@done:		clc
			rts
@fail:		sec
			rts
			
;----------------------------------------------------------
; File Open routines
;----------------------------------------------------------

; Open relative file
openrel:	; get record size
			lda param2+1				; check open mode
			bne	@p2ok
@p2nok:		jmp not_found				; record size not given, fail
@p2ok:		sta ptr+1
			lda param2
			sta ptr
			ldy #0
			lda (ptr), y
			beq @p2nok 
			sta param2
			; open dir entry
			lda filename
			sta ptr
			lda filename+1
			sta ptr+1
			jsr dir_find
			bcc @rel_open
			; create file			
			lda filename
			sta ptr
			lda filename+1
			sta ptr+1
			jsr dir_add
			bcc @set_rel
			lda #STATUS_DISK_FULL
			jsr cmd_setst
			ERROR_RETURN ERR_TOO_MANY_FILES
@set_rel:	lda #FILE_TYPE_RELATIVE
			ldy #DirEntry::type
			sta (ptr), y
@rel_open:	jsr openinfo
			bcc @infook
			ERROR_RETURN ERR_FILE_OPEN
@infook:	stx actinfo
			; copy size (low)
			ldy #DirEntry::losize
			lda (ptr), y
			sta finflsze, x
			; set open flag	
			ldy #DirEntry::type
			lda (ptr), y
			and #~FILE_FLAG_OPEN
			sta (ptr), y
			; set-up file position
			lda #0
			sta finfpos, x
			ldy #DirEntry::bank
			lda (ptr), y
			sta finfbank, x
			ldy #DirEntry::address
			lda (ptr), y
			sta finfaddr, x
			; set mode
			lda #IO_MODE_REL			
			sta finfmode, x
			; prepare rel access			
			ldy param2					; get record size
			lda $B9						; get channel no.
			jsr rel_open
			bcs @rclose
			rts
@rclose:	ldx actinfo
			lda #IO_MODE_READEOF			
			sta finfmode, x
			rts

; Open file for reading
openrd:		lda filename
			sta ptr
			lda filename+1
			sta ptr+1
			ldy #0
			lda (ptr), y
			cmp #'$'
			bne @notdir
			jmp opendir
@notdir:	jsr dir_find
			bcc @initfile
			jmp not_found
@initfile:	; init status
			jsr openinfo
			bcc @infook
			ERROR_RETURN ERR_FILE_OPEN
@infook:	stx actinfo
			; copy size (low) for reading
			ldy #DirEntry::losize
			lda (ptr), y
			sta finflsze, x
			; set open flag	
			ldy #DirEntry::type
			lda (ptr), y
			and #~FILE_FLAG_OPEN
			sta (ptr), y			
			; init read pointer
			ldy #DirEntry::bank
			lda (ptr), y
			pha
			ldy #DirEntry::address
			lda (ptr), y
			tay
			pla
			bne @not_empty
			lda #$40				; ST := EOF
			jsr $FE6A
			ldx actinfo
			lda #IO_MODE_READEOF 
			bne @goon
@not_empty:	sta finfbank, x
			tya
			sta finfaddr, x
			tay
			lda finflsze, x
			beq @block
			lda finfbank, x
			jsr md_next
			bcs @tail
@block:		lda #IO_MODE_READBLOCK
			.byte $2C
@tail:		lda #IO_MODE_READTAIL			
@goon:		; set read function
			ldx actinfo
			sta finfmode, x
			clc
			rts

; Open file for writing
openwr:		; check if file exists already
			lda filename
			sta ptr
			lda filename+1
			sta ptr+1
			ldy #0
			lda (ptr), y
			cmp #'$'
			beq @rodir
			jsr dir_find
			bcs @create
			; file exists, check for '@:'
			lda prefix+1
			beq @rodir
			sta ptr2+1
			lda prefix
			sta ptr2
			ldy #0
			lda (ptr2), y
			cmp #'@'
			bne @rodir
			; truncate file
			ldy #DirEntry::bank
			lda (ptr), y
			pha
			ldy #DirEntry::address
			lda (ptr), y
			tay
			pla
			jsr md_free
			; clear dir entry
			lda #0
			ldy #DirEntry::type
@clr_loop:	sta (ptr), y
			iny
			cpy #.sizeof(DirEntry)
			bne @clr_loop
			beq @init_wr
@rodir:		lda #STATUS_FILE_EXISTS
			jsr cmd_setst
			ERROR_RETURN ERR_NOT_OUTPUT_FILE
@create:	; create dir entry
			lda filename
			sta ptr
			lda filename+1
			sta ptr+1
			jsr dir_add
			bcc @init_wr
			lda #STATUS_DISK_FULL
			jsr cmd_setst
			ERROR_RETURN ERR_TOO_MANY_FILES
@init_wr:	; set file type
			ldx #FILE_TYPE_PROGRAM
@do_open:	lda param1+1				; check file type
			beq	@ftset					; not given, use default
			sta ptr2+1
			lda param1
			sta ptr2
			ldy #0
			lda (ptr2), y
			cmp #'s'
			bne @nd
			ldx #FILE_TYPE_SEQUENTIAL
			bne @ftset
@nd:		cmp #'p'
			beq @ftset
			cmp #'u'
			bne @nu
			ldx #FILE_TYPE_USER
			bne @ftset
@nu:		cmp #'l'
			bne @ftset
			ldx #FILE_TYPE_RELATIVE
@ftset:		txa							; set file type
			ldy #DirEntry::type
			sta (ptr), y			
			; init status
			jsr openinfo
			bcc @infook
			ERROR_RETURN ERR_FILE_OPEN
@infook:	stx actinfo
			; set I/O mode
initwr:		lda #IO_MODE_WRITEFIRST
			sta finfmode, x
			clc
			rts

; Open file for appending
openap:		jsr openrd
			bcs @ioerr
			jsr seekend
			; set I/O mode
			ldx actinfo
			lda finfbank, x
			beq initwr
			lda #IO_MODE_WRITENEXT
			sta finfmode, x
			clc
			rts
@ioerr:		sec
			rts

; Open directory for reading
opendir:	jsr dir_build
			jsr openinfo
			stx actinfo
			lda ptr2
			sta finfbank, x
			lda ptr2+1
			sta finfaddr, x
			; set read function
			lda #IO_MODE_READDIR
			sta finfmode, x
			clc
			rts

; Opens command channel 
opencmd:	jsr cmd_open
			lda #0
			sta ptr
			sta ptr+1
			jsr openinfo
			ldy $B7
			beq @done
			lda #<fname_buf
			sta ptr
			lda #>fname_buf
			sta ptr+1
			lda #$0
			sta (ptr), y
			txa
			pha
			jsr exec_cmd
			pla
			tax
			; set read function
@done:		lda #IO_MODE_COMMAND
			sta finfmode, x
			clc
			rts

;----------------------------------------------------------
; File info management
;----------------------------------------------------------

; Init file info for open
; INPUT:
;	(ptr)	... address of dir entry
;	$B8		... LFN
;	$B9		... channel
; OUTPUT:
;	X		... index of file info block
; RETURN:
;	Carry	... Success (0) or failure (1)
;
openinfo:	ldx #MAX_OPEN_FILES
			lda $B8
@chkloop:	cmp fileno-1, x
			beq @fail
			dex
			bne @chkloop
@fndloop:	lda fileno, x
			beq @found
			inx
			cpx #MAX_OPEN_FILES
			bne @fndloop
@fail:		sec						; -> file open or too many open files
			rts
@found:		lda $B8
			sta fileno, x
			tay
			txa
			sta finidx, y
			tax
			; clear info block
			lda #0
			sta finfbank, x
			sta finfaddr, x
			sta finfpos, x
			sta finflsze, x
			; set channel
			lda $B9
			sta finfchn, x
			; set file entry
			lda ptr
			sta finfent_lo, x
			lda ptr+1
			sta finfent_hi, x
			; set default handler
			lda #IO_MODE_KERNEL
			sta finfmode, x
			clc					
			rts

; Find file info for LFN
; INPUT:
;	A		... LFN
; OUTPUT:
;	X		... index of file info block
; RETURN:
;	Carry 	... Success (0) or failure (1)
;
findinfo:	tay
			ldx finidx, y
			cmp fileno, x
			beq @found
@fail:		sec						; -> LFN not found
			rts
@found:		clc
			rts

; Check channel type
; INPUT:
;	A 		... channel
; OUTPUT:
;	X		... file info index (if found)
; RETURN;
;	Carry 	... found (0) or not open (1)
;	Zero	... file pos. can be set (0) or not (1)

chkinfo:	ldx #0
			sta actchn
@fndloop:	lda fileno, x
			beq @next
			lda actchn
			cmp finfchn, x
			beq @found
@next:		inx
			cpx #MAX_OPEN_FILES
			bcc @fndloop
			sec
			rts
@found:		lda finfmode, x
			cmp #IO_MODE_READBLOCK
			beq	@done
			cmp #IO_MODE_READTAIL
			beq @done			
			cmp #IO_MODE_READEOF
			; bne @done
			; TODO: check for dir
@done:		clc
			rts

; Init file info ptr
;
initinfo:	ldx #0
			lda #0
@idxloop:	sta finidx, x
			dex
			bne @idxloop 
			rts

; Set FileInfo for writing
; INPUT
;	X ... info index
;
setinfout:	pha
			tya
			pha
			; copy data
			stx outinfo+FileInfo::index
			lda finfmode, x
			sta outinfo+FileInfo::mode
			lda finfbank, x
			sta outinfo+FileInfo::bank
			lda finfaddr, x
			sta outinfo+FileInfo::addr
			lda finfpos, x
			sta outinfo+FileInfo::pos
			lda finflsze, x
			sta outinfo+FileInfo::losize
			; set ptr to directory entry
			lda finfent_hi, x
			beq @done						; no dir entry (e.g. command channel)
			sta ptr+1
			lda finfent_lo, x
			sta ptr
			; get blocks
			ldy #DirEntry::blocks
			lda (ptr), y
			sta outinfo+FileInfo::blocks
			iny
			lda (ptr), y
			sta outinfo+FileInfo::blocks+1
@done:		pla
			tay
			pla
			rts

; Set FileInfo for reading
; INPUT:
;	X ... info index
;
setinfin:	pha
			tya
			pha
			; copy data
			stx ininfo+FileInfo::index
			lda finfmode, x
			sta ininfo+FileInfo::mode
			lda finfbank, x
			sta ininfo+FileInfo::bank
			lda finfaddr, x
			sta ininfo+FileInfo::addr
			lda finfpos, x
			sta ininfo+FileInfo::pos
			lda finflsze, x
			sta ininfo+FileInfo::losize
			; set dirptr to directory entry
			lda finfent_lo, x
			sta ininfo+FileInfo::dirptr
			lda finfent_hi, x
			sta ininfo+FileInfo::dirptr+1
			pla
			tay
			pla
			rts

; Get FileInfo for writing
; OUTPUT:
;	X ... info index
;
getinfout:	pha
			tya
			pha
			ldx outinfo+FileInfo::index
			bmi @done
			; copy data
			lda outinfo+FileInfo::mode
			sta finfmode, x
			lda outinfo+FileInfo::bank
			sta finfbank, x
			lda outinfo+FileInfo::addr
			sta finfaddr, x
			lda outinfo+FileInfo::pos
			sta finfpos, x
			lda outinfo+FileInfo::losize
			sta finflsze, x
			; set ptr to directory entry
			lda finfent_lo, x
			sta ptr
			lda finfent_hi, x
			sta ptr+1
			; update blocks
			ldy #DirEntry::blocks
			lda outinfo+FileInfo::blocks
			sta (ptr), y
			iny
			lda outinfo+FileInfo::blocks+1
			sta (ptr), y
			; update losize
			ldy #DirEntry::losize
			lda outinfo+FileInfo::losize
			sta (ptr), y			
@done:		pla
			tay
			pla
			rts

; Get FileInfo for reading
; OUTPUT:
;	X ... info index
;
getinfin:	pha
			tya
			pha
			ldx ininfo+FileInfo::index
			bmi @done
			; copy data
			lda ininfo+FileInfo::mode
			sta finfmode, x
			lda ininfo+FileInfo::bank
			sta finfbank, x
			lda ininfo+FileInfo::addr
			sta finfaddr, x
			lda ininfo+FileInfo::pos
			sta finfpos, x
			lda ininfo+FileInfo::losize
			sta finflsze, x
			; get dir ptr
			lda ininfo+FileInfo::dirptr
			sta finfent_lo, x
			lda ininfo+FileInfo::dirptr+1
			sta finfent_hi, x
@done:		pla
			tay
			pla
			rts

; clear info
clrinfo:	lda #$FF
			sta ininfo+FileInfo::index
			sta outinfo+FileInfo::index
			rts

;----------------------------------------------------------
upd_dir_ptr:
			ldx outinfo+FileInfo::index
			; set ptr to directory entry
			lda finfent_lo, x
			sta ptr
			lda finfent_hi, x
			sta ptr+1
			; update ptr to first block
			ldy #DirEntry::bank
			lda outinfo+FileInfo::bank
			sta (ptr), y
			ldy #DirEntry::address
			lda outinfo+FileInfo::addr
			sta (ptr), y
			rts

; seeks to file end
seekend:	; selects block in cache
			lda finfbank, x
			beq @eof
			ldy finfaddr, x
			stx actinfo
			jsr md_next
			bcs @eof
@loop:		cpx #0
			beq @done
			jsr md_next
			bcc @loop
@done:		ldx actinfo
			sta finfbank, x
			tya
			sta finfaddr, x
@eof:		ldy #DirEntry::losize
			lda (ptr), y
			sta finfpos, x
			rts	

;----------------------------------------------------------
; Set file position
; INPUT:
;   (ptr2) ... position low word
;	A/Y ...... position high word
;	X ........ file index.
;
fsetpos:	stx posidx
			cpy #0
			beq @hiok
			lda #STATUS_RECORD_NOT_PRESENT
			jmp cmd_setst
@hiok:		sta hbyte
			; set ptr to directory entry
			lda finfent_lo, x
			sta ptr
			lda finfent_hi, x
			sta ptr+1
			; check blocksize
			lda hbyte
			ldy #DirEntry::blocks+1
			cmp (ptr), y
			bcc @insize 
			bne @outrange
			dey
			lda ptr2+1
			cmp (ptr), y
			bcc @insize
			bne @outrange
			; check losize
			ldy #DirEntry::losize
			lda ptr2
			cmp (ptr), y
			bcc @insize						
@outrange:	lda #STATUS_RECORD_NOT_PRESENT
			jmp cmd_setst
@insize:	; copy size (low) for reading
			ldy #DirEntry::losize
			lda (ptr), y
			sta finflsze, x
			; get head pointer
			ldy #DirEntry::bank
			lda (ptr), y
			pha
			ldy #DirEntry::address
			lda (ptr), y
			tay
			pla
			; find block
			inc hbyte
			inc ptr2+1
@nloop:		dec ptr2+1
			bne @donext
			dec hbyte
			beq @done
@donext:	jsr md_next
			jmp @nloop
@done:		stx lblkflg
			; set block
			ldx posidx
			sta finfbank, x
			tya
			sta finfaddr, x
			; set low position
			lda ptr2
			sta finfpos, x
			; init read-mode & losize
			lda #0
			cmp lblkflg
			beq @tail
@block:		lda #IO_MODE_READBLOCK
			.byte $2C
@tail:		lda #IO_MODE_READTAIL			
			sta finfmode, x
			; if active init ininfo
			cpx ininfo+FileInfo::index
			bne @notactive
			jsr setinfin
			lda finfmode, x
			tax
			jsr io_setin
			jsr io_prepin
@notactive:	; done
			lda #STATUS_OK
			jmp cmd_setst

	.segment "DATA"

hbyte:		.res 1	; highest byte of address (24bit)
posidx:		.res 1	; index for finfo
lblkflg:	.res 1	; indicates if last block or not (0)
